/*-------------------------------------------------------------------------
 *
 * array_userfuncs.c
 *	  Misc 用户可见的数组支持函数
 *
 * Copyright (c) 2003-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/array_userfuncs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "common/int.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"


static Datum fc_array_position_common(FunctionCallInfo fcinfo);


/*
 * fetch_array_arg_replace_nulls
 *
 * 获取以展开形式出现的数组值参数；如果它为null，则构造一个适当数据类型的空数组值。同时在fn_extra中缓存基本元素类型信息。
 *
 * 注意：如果输入是可读/可写指针，则返回输入参数；因此调用者必须确保他们的更改是“安全的”，即他们无法让数组处于损坏状态。
 *
 * 如果我们作为聚合函数被调用，请确保任何新创建的展开数组是在聚合状态上下文中分配的，以节省复制操作。
 */
static ExpandedArrayHeader *
fc_fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int fc_argno)
{
	ExpandedArrayHeader *fc_eah;
	Oid			fc_element_type;
	ArrayMetaState *fc_my_extra;
	MemoryContext fc_resultcxt;

	/* 如果第一次通过，创建数据类型缓存结构 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fc_my_extra = (ArrayMetaState *)
			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
							   sizeof(ArrayMetaState));
		fc_my_extra->element_type = InvalidOid;
		fcinfo->flinfo->fn_extra = fc_my_extra;
	}

	/* 确定我们希望结果在哪个上下文中 */
	if (!AggCheckCallContext(fcinfo, &fc_resultcxt))
		fc_resultcxt = CurrentMemoryContext;

	/* 现在收集数组值 */
	if (!PG_ARGISNULL(fc_argno))
	{
		MemoryContext fc_oldcxt = MemoryContextSwitchTo(fc_resultcxt);

		fc_eah = PG_GETARG_EXPANDED_ARRAYX(fc_argno, fc_my_extra);
		MemoryContextSwitchTo(fc_oldcxt);
	}
	else
	{
		/* 我们必须查找数组类型和元素类型 */
		Oid			fc_arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, fc_argno);

		if (!OidIsValid(fc_arr_typeid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("could not determine input data type")));
		fc_element_type = get_element_type(fc_arr_typeid);
		if (!OidIsValid(fc_element_type))
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("input data type is not an array")));

		fc_eah = construct_empty_expanded_array(fc_element_type,
											 fc_resultcxt,
											 fc_my_extra);
	}

	return fc_eah;
}

/* -----------------------------------------------------------------------------
 * array_append :
 *		将一个元素推入一维数组的末尾
 *----------------------------------------------------------------------------
 */
Datum array_append(PG_FUNCTION_ARGS)
{
	ExpandedArrayHeader *fc_eah;
	Datum		fc_newelem;
	bool		fc_isNull;
	Datum		fc_result;
	int		   *fc_dimv,
			   *fc_lb;
	int			fc_indx;
	ArrayMetaState *fc_my_extra;

	fc_eah = fc_fetch_array_arg_replace_nulls(fcinfo, 0);
	fc_isNull = PG_ARGISNULL(1);
	if (fc_isNull)
		fc_newelem = (Datum) 0;
	else
		fc_newelem = PG_GETARG_DATUM(1);

	if (fc_eah->ndims == 1)
	{
		/* 添加新元素 */
		fc_lb = fc_eah->lbound;
		fc_dimv = fc_eah->dims;

		/* 添加元素的索引在 lb[0] + (dimv[0] - 1) + 1 处 */
		if (pg_add_s32_overflow(fc_lb[0], fc_dimv[0], &fc_indx))
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("integer out of range")));
	}
	else if (fc_eah->ndims == 0)
		fc_indx = 1;
	else
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("argument must be empty or one-dimensional array")));

	/* 执行元素插入 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;

	fc_result = array_set_element(EOHPGetRWDatum(&fc_eah->hdr),
							   1, &fc_indx, fc_newelem, fc_isNull,
							   -1, fc_my_extra->typlen, fc_my_extra->typbyval, fc_my_extra->typalign);

	PG_RETURN_DATUM(fc_result);
}

/*-----------------------------------------------------------------------------
 * array_prepend :
 *		将一个元素推送到一维数组的前面
 *----------------------------------------------------------------------------
 */
Datum array_prepend(PG_FUNCTION_ARGS)
{
	ExpandedArrayHeader *fc_eah;
	Datum		fc_newelem;
	bool		fc_isNull;
	Datum		fc_result;
	int		   *fc_lb;
	int			fc_indx;
	int			fc_lb0;
	ArrayMetaState *fc_my_extra;

	fc_isNull = PG_ARGISNULL(0);
	if (fc_isNull)
		fc_newelem = (Datum) 0;
	else
		fc_newelem = PG_GETARG_DATUM(0);
	fc_eah = fc_fetch_array_arg_replace_nulls(fcinfo, 1);

	if (fc_eah->ndims == 1)
	{
		/* 在前面添加新元素 */
		fc_lb = fc_eah->lbound;
		fc_lb0 = fc_lb[0];

		if (pg_sub_s32_overflow(fc_lb0, 1, &fc_indx))
			ereport(ERROR,
					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
					 errmsg("integer out of range")));
	}
	else if (fc_eah->ndims == 0)
	{
		fc_indx = 1;
		fc_lb0 = 1;
	}
	else
		ereport(ERROR,
				(errcode(ERRCODE_DATA_EXCEPTION),
				 errmsg("argument must be empty or one-dimensional array")));

	/* 执行元素插入 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;

	fc_result = array_set_element(EOHPGetRWDatum(&fc_eah->hdr),
							   1, &fc_indx, fc_newelem, fc_isNull,
							   -1, fc_my_extra->typlen, fc_my_extra->typbyval, fc_my_extra->typalign);

	/* 调整结果的 LB 以匹配输入的，符合 prepend 的预期 */
	Assert(fc_result == EOHPGetRWDatum(&fc_eah->hdr));
	if (fc_eah->ndims == 1)
	{
		/* 无论我们是否进行了解构，这样做都是可以的 */
		fc_eah->lbound[0] = fc_lb0;
	}

	PG_RETURN_DATUM(fc_result);
}

/*-----------------------------------------------------------------------------
 * array_cat :
 *		连接两个 nD 数组形成一个 nD 数组，或者
 *		将一个 (n-1)D 数组推送到一个 nD 数组的末尾
 *----------------------------------------------------------------------------
 */
Datum array_cat(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_v1,
			   *fc_v2;
	ArrayType  *fc_result;
	int		   *fc_dims,
			   *fc_lbs,
				fc_ndims,
				fc_nitems,
				fc_ndatabytes,
				fc_nbytes;
	int		   *fc_dims1,
			   *fc_lbs1,
				fc_ndims1,
				fc_nitems1,
				fc_ndatabytes1;
	int		   *fc_dims2,
			   *fc_lbs2,
				fc_ndims2,
				fc_nitems2,
				fc_ndatabytes2;
	int			fc_i;
	char	   *fc_dat1,
			   *fc_dat2;
	bits8	   *fc_bitmap1,
			   *fc_bitmap2;
	Oid			fc_element_type;
	Oid			fc_element_type1;
	Oid			fc_element_type2;
	int32		fc_dataoffset;

	/* 连接一个空数组是无操作，只需返回另一个输入 */
	if (PG_ARGISNULL(0))
	{
		if (PG_ARGISNULL(1))
			PG_RETURN_NULL();
		fc_result = PG_GETARG_ARRAYTYPE_P(1);
		PG_RETURN_ARRAYTYPE_P(fc_result);
	}
	if (PG_ARGISNULL(1))
	{
		fc_result = PG_GETARG_ARRAYTYPE_P(0);
		PG_RETURN_ARRAYTYPE_P(fc_result);
	}

	fc_v1 = PG_GETARG_ARRAYTYPE_P(0);
	fc_v2 = PG_GETARG_ARRAYTYPE_P(1);

	fc_element_type1 = ARR_ELEMTYPE(fc_v1);
	fc_element_type2 = ARR_ELEMTYPE(fc_v2);

	/* 检查我们是否有匹配的元素类型 */
	if (fc_element_type1 != fc_element_type2)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("cannot concatenate incompatible arrays"),
				 errdetail("Arrays with element types %s and %s are not "
						   "compatible for concatenation.",
						   format_type_be(fc_element_type1),
						   format_type_be(fc_element_type2))));

	/* 可以，使用它 */
	fc_element_type = fc_element_type1;

	/*----------
	 * 我们必须有以下其中一种输入组合：
	 * 1) 一个空数组和一个非空数组
	 * 2) 两个数组都是空的
	 * 3) 两个数组的 ndims1 == ndims2
	 * 4) ndims1 == ndims2 - 1
	 * 5) ndims1 == ndims2 + 1
	 *----------
	 */
	fc_ndims1 = ARR_NDIM(fc_v1);
	fc_ndims2 = ARR_NDIM(fc_v2);

	/*
	 * 短路 - 如果一个输入数组是空的，而另一个不是，我们
	 * 返回非空的那个作为结果
	 *
	 * 如果两个都是空的，返回第一个
	 */
	if (fc_ndims1 == 0 && fc_ndims2 > 0)
		PG_RETURN_ARRAYTYPE_P(fc_v2);

	if (fc_ndims2 == 0)
		PG_RETURN_ARRAYTYPE_P(fc_v1);

	/* 剩下的归于规则 3、4 或 5 */
	if (fc_ndims1 != fc_ndims2 &&
		fc_ndims1 != fc_ndims2 - 1 &&
		fc_ndims1 != fc_ndims2 + 1)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("cannot concatenate incompatible arrays"),
				 errdetail("Arrays of %d and %d dimensions are not "
						   "compatible for concatenation.",
						   fc_ndims1, fc_ndims2)));

	/* 获取参数数组的详细信息 */
	fc_lbs1 = ARR_LBOUND(fc_v1);
	fc_lbs2 = ARR_LBOUND(fc_v2);
	fc_dims1 = ARR_DIMS(fc_v1);
	fc_dims2 = ARR_DIMS(fc_v2);
	fc_dat1 = ARR_DATA_PTR(fc_v1);
	fc_dat2 = ARR_DATA_PTR(fc_v2);
	fc_bitmap1 = ARR_NULLBITMAP(fc_v1);
	fc_bitmap2 = ARR_NULLBITMAP(fc_v2);
	fc_nitems1 = ArrayGetNItems(fc_ndims1, fc_dims1);
	fc_nitems2 = ArrayGetNItems(fc_ndims2, fc_dims2);
	fc_ndatabytes1 = ARR_SIZE(fc_v1) - ARR_DATA_OFFSET(fc_v1);
	fc_ndatabytes2 = ARR_SIZE(fc_v2) - ARR_DATA_OFFSET(fc_v2);

	if (fc_ndims1 == fc_ndims2)
	{
		/*
		 * 结果数组由输入参数数组的元素（可能是数组本身）
		 * 组成
		 */
		fc_ndims = fc_ndims1;
		fc_dims = (int *) palloc(fc_ndims * sizeof(int));
		fc_lbs = (int *) palloc(fc_ndims * sizeof(int));

		fc_dims[0] = fc_dims1[0] + fc_dims2[0];
		fc_lbs[0] = fc_lbs1[0];

		for (fc_i = 1; fc_i < fc_ndims; fc_i++)
		{
			if (fc_dims1[fc_i] != fc_dims2[fc_i] || fc_lbs1[fc_i] != fc_lbs2[fc_i])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("cannot concatenate incompatible arrays"),
						 errdetail("Arrays with differing element dimensions are "
								   "not compatible for concatenation.")));

			fc_dims[fc_i] = fc_dims1[fc_i];
			fc_lbs[fc_i] = fc_lbs1[fc_i];
		}
	}
	else if (fc_ndims1 == fc_ndims2 - 1)
	{
		/*
		 * 结果数组的第二个参数作为外部数组，第一个参数
		 * 插入在外部维度的前面
		 */
		fc_ndims = fc_ndims2;
		fc_dims = (int *) palloc(fc_ndims * sizeof(int));
		fc_lbs = (int *) palloc(fc_ndims * sizeof(int));
		memcpy(fc_dims, fc_dims2, fc_ndims * sizeof(int));
		memcpy(fc_lbs, fc_lbs2, fc_ndims * sizeof(int));

		/* 增加外部数组中的元素数量 */
		fc_dims[0] += 1;

		/* 确保添加的元素与我们现有的元素匹配 */
		for (fc_i = 0; fc_i < fc_ndims1; fc_i++)
		{
			if (fc_dims1[fc_i] != fc_dims[fc_i + 1] || fc_lbs1[fc_i] != fc_lbs[fc_i + 1])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("cannot concatenate incompatible arrays"),
						 errdetail("Arrays with differing dimensions are not "
								   "compatible for concatenation.")));
		}
	}
	else
	{
		/*
		 * (ndims1 == ndims2 + 1)
		 *
		 * 结果数组的第一个参数作为外部数组，第二个参数
		 * 附加到外部维度的末尾
		 */
		fc_ndims = fc_ndims1;
		fc_dims = (int *) palloc(fc_ndims * sizeof(int));
		fc_lbs = (int *) palloc(fc_ndims * sizeof(int));
		memcpy(fc_dims, fc_dims1, fc_ndims * sizeof(int));
		memcpy(fc_lbs, fc_lbs1, fc_ndims * sizeof(int));

		/* 增加外部数组中的元素数量 */
		fc_dims[0] += 1;

		/* 确保添加的元素与我们现有的元素匹配 */
		for (fc_i = 0; fc_i < fc_ndims2; fc_i++)
		{
			if (fc_dims2[fc_i] != fc_dims[fc_i + 1] || fc_lbs2[fc_i] != fc_lbs[fc_i + 1])
				ereport(ERROR,
						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
						 errmsg("cannot concatenate incompatible arrays"),
						 errdetail("Arrays with differing dimensions are not "
								   "compatible for concatenation.")));
		}
	}

	/* 主要为溢出检查执行此操作 */
	fc_nitems = ArrayGetNItems(fc_ndims, fc_dims);
	ArrayCheckBounds(fc_ndims, fc_dims, fc_lbs);

	/* 构建结果数组 */
	fc_ndatabytes = fc_ndatabytes1 + fc_ndatabytes2;
	if (ARR_HASNULL(fc_v1) || ARR_HASNULL(fc_v2))
	{
		fc_dataoffset = ARR_OVERHEAD_WITHNULLS(fc_ndims, fc_nitems);
		fc_nbytes = fc_ndatabytes + fc_dataoffset;
	}
	else
	{
		fc_dataoffset = 0;			/* 表示没有空位图的标记 */
		fc_nbytes = fc_ndatabytes + ARR_OVERHEAD_NONULLS(fc_ndims);
	}
	fc_result = (ArrayType *) palloc0(fc_nbytes);
	SET_VARSIZE(fc_result, fc_nbytes);
	fc_result->ndim = fc_ndims;
	fc_result->dataoffset = fc_dataoffset;
	fc_result->elemtype = fc_element_type;
	memcpy(ARR_DIMS(fc_result), fc_dims, fc_ndims * sizeof(int));
	memcpy(ARR_LBOUND(fc_result), fc_lbs, fc_ndims * sizeof(int));
	/* 数据区是 arg1 然后是 arg2 */
	memcpy(ARR_DATA_PTR(fc_result), fc_dat1, fc_ndatabytes1);
	memcpy(ARR_DATA_PTR(fc_result) + fc_ndatabytes1, fc_dat2, fc_ndatabytes2);
	/* 如有需要处理空位图 */
	if (ARR_HASNULL(fc_result))
	{
		array_bitmap_copy(ARR_NULLBITMAP(fc_result), 0,
						  fc_bitmap1, 0,
						  fc_nitems1);
		array_bitmap_copy(ARR_NULLBITMAP(fc_result), fc_nitems1,
						  fc_bitmap2, 0,
						  fc_nitems2);
	}

	PG_RETURN_ARRAYTYPE_P(fc_result);
}


/*
 * ARRAY_AGG(anynonarray) 聚合函数
 */
Datum array_agg_transfn(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
	MemoryContext fc_aggcontext;
	ArrayBuildState *fc_state;
	Datum		fc_elem;

	if (fc_arg1_typeid == InvalidOid)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("could not determine input data type")));

	/*
	 * 注意：我们不需要运行时检查 arg1_typeid 是否有效的
	 * 数组元素类型，因为解析器在解析该多态聚合的输入/输出类型时
	 * 将验证这一点。
	 */

	if (!AggCheckCallContext(fcinfo, &fc_aggcontext))
	{
		/* 由于内部类型参数，无法直接调用 */
		elog(ERROR, "array_agg_transfn called in non-aggregate context");
	}

	if (PG_ARGISNULL(0))
		fc_state = initArrayResult(fc_arg1_typeid, fc_aggcontext, false);
	else
		fc_state = (ArrayBuildState *) PG_GETARG_POINTER(0);

	fc_elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);

	fc_state = accumArrayResult(fc_state,
							 fc_elem,
							 PG_ARGISNULL(1),
							 fc_arg1_typeid,
							 fc_aggcontext);

	/*
	 * array_agg() 的过渡类型声明为“内部”，这是一种与指针大小相同的按值传递类型。
	 * 因此，我们可以安全地通过 nodeAgg.c 的机制传递
	 * ArrayBuildState 指针。
	 */
	PG_RETURN_POINTER(fc_state);
}

Datum array_agg_finalfn(PG_FUNCTION_ARGS)
{
	Datum		fc_result;
	ArrayBuildState *fc_state;
	int			fc_dims[1];
	int			fc_lbs[1];

	/* 由于内部类型参数，无法直接调用 */
	Assert(AggCheckCallContext(fcinfo, NULL));

	fc_state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);

	if (fc_state == NULL)
		PG_RETURN_NULL();		/* 如果没有输入值则返回空 */

	fc_dims[0] = fc_state->nelems;
	fc_lbs[0] = 1;

	/*
	 * 生成结果。我们不能释放 ArrayBuildState，因为
	 * 有时聚合最终函数会重新执行。相反，这是
	 * nodeAgg.c 的责任，在安全时重置 aggcontext。
	 */
	fc_result = makeMdArrayResult(fc_state, 1, fc_dims, fc_lbs,
							   CurrentMemoryContext,
							   false);

	PG_RETURN_DATUM(fc_result);
}

/*
 * ARRAY_AGG(anyarray) 聚合函数
 */
Datum array_agg_array_transfn(PG_FUNCTION_ARGS)
{
	Oid			fc_arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
	MemoryContext fc_aggcontext;
	ArrayBuildStateArr *fc_state;

	if (fc_arg1_typeid == InvalidOid)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
				 errmsg("could not determine input data type")));

	/*
	 * 注意：我们不需要在运行时检查arg1_typeid是否是一个有效的数组类型，
	 * 因为解析器在解析这个多态聚合的输入/结果类型时会验证这一点。
	 */

	if (!AggCheckCallContext(fcinfo, &fc_aggcontext))
	{
		/* 由于内部类型参数，无法直接调用 */
		elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
	}


	if (PG_ARGISNULL(0))
		fc_state = initArrayResultArr(fc_arg1_typeid, InvalidOid, fc_aggcontext, false);
	else
		fc_state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);

	fc_state = accumArrayResultArr(fc_state,
								PG_GETARG_DATUM(1),
								PG_ARGISNULL(1),
								fc_arg1_typeid,
								fc_aggcontext);

	/*
	 * array_agg()的过渡类型声明为“内部”，这是一种按值传递的类型，大小与指针相同。
	 * 所以我们可以安全地通过nodeAgg.c的处理传递ArrayBuildStateArr指针。
	 */
	PG_RETURN_POINTER(fc_state);
}

Datum array_agg_array_finalfn(PG_FUNCTION_ARGS)
{
	Datum		fc_result;
	ArrayBuildStateArr *fc_state;

	/* 由于内部类型参数，无法直接调用 */
	Assert(AggCheckCallContext(fcinfo, NULL));

	fc_state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);

	if (fc_state == NULL)
		PG_RETURN_NULL();		/* 如果没有输入值则返回空 */

	/*
	 * 制作结果。我们不能释放ArrayBuildStateArr，因为
	 * 有时聚合最终函数会被重新执行。相反，由nodeAgg.c负责在安全时
	 * 重新设置aggcontext。
	 */
	fc_result = makeArrayResultArr(fc_state, CurrentMemoryContext, false);

	PG_RETURN_DATUM(fc_result);
}

/*-----------------------------------------------------------------------------
 * array_position, array_position_start :
 *			返回数组中值的偏移量。
 *
 * 使用IS NOT DISTINCT FROM语义进行比较。值未找到时返回NULL。
 *-----------------------------------------------------------------------------
 */
Datum array_position(PG_FUNCTION_ARGS)
{
	return fc_array_position_common(fcinfo);
}

Datum array_position_start(PG_FUNCTION_ARGS)
{
	return fc_array_position_common(fcinfo);
}

/*
 * array_position_common
 *		array_position和array_position_start的公共代码
 *
 * 这些是单独的包装器，以便进行opr_sanity回归测试。
 * 它们不是严格的，因此我们必须显式测试空输入。
 */
static Datum fc_array_position_common(FunctionCallInfo fcinfo)
{
	ArrayType  *fc_array;
	Oid			fc_collation = PG_GET_COLLATION();
	Oid			fc_element_type;
	Datum		fc_searched_element,
				fc_value;
	bool		fc_isnull;
	int			fc_position,
				fc_position_min;
	bool		fc_found = false;
	TypeCacheEntry *fc_typentry;
	ArrayMetaState *fc_my_extra;
	bool		fc_null_search;
	ArrayIterator fc_array_iterator;

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_array = PG_GETARG_ARRAYTYPE_P(0);

	/*
	 * 我们拒绝在多维数组中搜索元素，因为我们
	 * 无法很好地报告数组中元素的位置。
	 */
	if (ARR_NDIM(fc_array) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("searching for elements in multidimensional arrays is not supported")));

	/* 在空数组中搜索是明确定义的：总是失败 */
	if (ARR_NDIM(fc_array) < 1)
		PG_RETURN_NULL();

	if (PG_ARGISNULL(1))
	{
		/* 当数组没有空值时快速返回 */
		if (!array_contains_nulls(fc_array))
			PG_RETURN_NULL();
		fc_searched_element = (Datum) 0;
		fc_null_search = true;
	}
	else
	{
		fc_searched_element = PG_GETARG_DATUM(1);
		fc_null_search = false;
	}

	fc_element_type = ARR_ELEMTYPE(fc_array);
	fc_position = (ARR_LBOUND(fc_array))[0] - 1;

	/* 确定从何处开始 */
	if (PG_NARGS() == 3)
	{
		if (PG_ARGISNULL(2))
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("initial position must not be null")));

		fc_position_min = PG_GETARG_INT32(2);
	}
	else
		fc_position_min = (ARR_LBOUND(fc_array))[0];

	/*
	 * 我们安排在每系列调用中仅查找一次array_create_iterator的类型信息，
	 * 假设元素类型不会在我们之下更改。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = ~fc_element_type;
	}

	if (fc_my_extra->element_type != fc_element_type)
	{
		get_typlenbyvalalign(fc_element_type,
							 &fc_my_extra->typlen,
							 &fc_my_extra->typbyval,
							 &fc_my_extra->typalign);

		fc_typentry = lookup_type_cache(fc_element_type, TYPECACHE_EQ_OPR_FINFO);

		if (!OidIsValid(fc_typentry->eq_opr_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify an equality operator for type %s",
							format_type_be(fc_element_type))));

		fc_my_extra->element_type = fc_element_type;
		fmgr_info_cxt(fc_typentry->eq_opr_finfo.fn_oid, &fc_my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
	}

	/* 检查每个数组元素，直到找到匹配项。 */
	fc_array_iterator = array_create_iterator(fc_array, 0, fc_my_extra);
	while (array_iterate(fc_array_iterator, &fc_value, &fc_isnull))
	{
		fc_position++;

		/* 如果调用者要求，跳过初始元素 */
		if (fc_position < fc_position_min)
			continue;

		/*
		 * 如果数组元素的值为null，则无法查看；但如果我们
		 * 搜索null，我们就有了一个匹配项并完成了。
		 */
		if (fc_isnull || fc_null_search)
		{
			if (fc_isnull && fc_null_search)
			{
				fc_found = true;
				break;
			}
			else
				continue;
		}

		/* 不是空值，因此运行运算符 */
		if (DatumGetBool(FunctionCall2Coll(&fc_my_extra->proc, fc_collation,
										   fc_searched_element, fc_value)))
		{
			fc_found = true;
			break;
		}
	}

	array_free_iterator(fc_array_iterator);

	/* 当输入被转换时避免内存泄漏 */
	PG_FREE_IF_COPY(fc_array, 0);

	if (!fc_found)
		PG_RETURN_NULL();

	PG_RETURN_INT32(fc_position);
}

/*-----------------------------------------------------------------------------
 * array_positions :
 *			返回数组中值位置的数组。
 *
 * 使用IS NOT DISTINCT FROM语义进行比较。当输入数组为NULL时返回NULL。
 * 当数组中未找到该值时，返回一个空数组。
 *
 * 这不是严格的，因此我们必须显式测试空输入。
 *-----------------------------------------------------------------------------
 */
Datum array_positions(PG_FUNCTION_ARGS)
{
	ArrayType  *fc_array;
	Oid			fc_collation = PG_GET_COLLATION();
	Oid			fc_element_type;
	Datum		fc_searched_element,
				fc_value;
	bool		fc_isnull;
	int			fc_position;
	TypeCacheEntry *fc_typentry;
	ArrayMetaState *fc_my_extra;
	bool		fc_null_search;
	ArrayIterator fc_array_iterator;
	ArrayBuildState *fc_astate = NULL;

	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_array = PG_GETARG_ARRAYTYPE_P(0);

	/*
	 * 我们拒绝在多维数组中搜索元素，因为我们
	 * 无法很好地报告数组中元素的位置。
	 */
	if (ARR_NDIM(fc_array) > 1)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("searching for elements in multidimensional arrays is not supported")));

	fc_astate = initArrayResult(INT4OID, CurrentMemoryContext, false);

	/* 在空数组中搜索是明确定义的：总是失败 */
	if (ARR_NDIM(fc_array) < 1)
		PG_RETURN_DATUM(makeArrayResult(fc_astate, CurrentMemoryContext));

	if (PG_ARGISNULL(1))
	{
		/* 当数组没有空值时快速返回 */
		if (!array_contains_nulls(fc_array))
			PG_RETURN_DATUM(makeArrayResult(fc_astate, CurrentMemoryContext));
		fc_searched_element = (Datum) 0;
		fc_null_search = true;
	}
	else
	{
		fc_searched_element = PG_GETARG_DATUM(1);
		fc_null_search = false;
	}

	fc_element_type = ARR_ELEMTYPE(fc_array);
	fc_position = (ARR_LBOUND(fc_array))[0] - 1;

	/*
	 * 我们安排在每系列调用中仅查找一次array_create_iterator的类型信息，
	 * 假设元素类型不会在我们之下更改。
	 */
	fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
	if (fc_my_extra == NULL)
	{
		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
													  sizeof(ArrayMetaState));
		fc_my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
		fc_my_extra->element_type = ~fc_element_type;
	}

	if (fc_my_extra->element_type != fc_element_type)
	{
		get_typlenbyvalalign(fc_element_type,
							 &fc_my_extra->typlen,
							 &fc_my_extra->typbyval,
							 &fc_my_extra->typalign);

		fc_typentry = lookup_type_cache(fc_element_type, TYPECACHE_EQ_OPR_FINFO);

		if (!OidIsValid(fc_typentry->eq_opr_finfo.fn_oid))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_FUNCTION),
					 errmsg("could not identify an equality operator for type %s",
							format_type_be(fc_element_type))));

		fc_my_extra->element_type = fc_element_type;
		fmgr_info_cxt(fc_typentry->eq_opr_finfo.fn_oid, &fc_my_extra->proc,
					  fcinfo->flinfo->fn_mcxt);
	}

	/*
	 * 仅在元素匹配给定元素时累积每个数组位置。
	 */
	fc_array_iterator = array_create_iterator(fc_array, 0, fc_my_extra);
	while (array_iterate(fc_array_iterator, &fc_value, &fc_isnull))
	{
		fc_position += 1;

		/*
		 * 如果数组元素的值为null，则无法查看；但如果我们
		 * 搜索null，我们就有了一个匹配项。
		 */
		if (fc_isnull || fc_null_search)
		{
			if (fc_isnull && fc_null_search)
				fc_astate =
					accumArrayResult(fc_astate, Int32GetDatum(fc_position), false,
									 INT4OID, CurrentMemoryContext);

			continue;
		}

		/* 不是空值，因此运行运算符 */
		if (DatumGetBool(FunctionCall2Coll(&fc_my_extra->proc, fc_collation,
										   fc_searched_element, fc_value)))
			fc_astate =
				accumArrayResult(fc_astate, Int32GetDatum(fc_position), false,
								 INT4OID, CurrentMemoryContext);
	}

	array_free_iterator(fc_array_iterator);

	/* 当输入被转换时避免内存泄漏 */
	PG_FREE_IF_COPY(fc_array, 0);

	PG_RETURN_DATUM(makeArrayResult(fc_astate, CurrentMemoryContext));
}
